package com.panopset.swing;

import static com.panopset.Util.getCanonicalPath;
import static com.panopset.Util.x;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Map.Entry;

import com.panopset.Util;
import com.panopset.UtilIO;
import com.panopset.io.LockedWriter;

/**
 * Document to GUI Scaffolding.
 *
 * @author Karl Dinwiddie.
 *
 */
public final class Scaffolding {
    /**
     * Set this to true to turn off write safety feature.
     */
    public static boolean trusted = false;

    /**
     * Scaffolding.
     */
    public static Scaffolding s = new Scaffolding();

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doNew() {
        userChooseAppDirectory();
        flush();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doOpen() {
        flush();
        userChooseAppDirectory();
        load();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doSave() {
        flush();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doSaveAs() {
        flush();
        userChooseAppDirectory();
        flush();
    }

    void reset() {
        Field.reset2dft();
    }
    /**
     * Get a value from the current map document.
     *
     * @param key
     *            Key.
     * @return Value.
     */
    public String get(final String key) {
        return map.get(key);
    }

    private void userChooseAppDirectory() {
        File newFile = com.panopset.UtilIO.getDirectoryFromUser(getHome(),
                x("Please select application directory"));
        try {
            setHome(newFile);
        } catch (IOException e) {
            Util.log(e);
        }
    }

    /**
     * Put a value to the current map document.
     *
     * @param key
     *            Key.
     * @param value
     *            Value.
     */
    public void put(final String key, final String value) {
        map.put(key, value);
    }

    private final Map<String, String> map = new HashMap<String, String>();

    private File dir;

    private String appName;

    public String getAppName() {
        if (appName == null) {
            if (App.app == null) {
                appName = "Scaffolding";
            } else {
                appName = App.app.getApplicationName();
            }
        }
        return appName;
    }

    /**
     * @param name Application name.
     */
    public void setAppName(final String name) {
        if (appName != null && !name.equals(appName)) {
            throw new RuntimeException("Logic error.");
        }
        appName = name;
    }

    /**
     * @param directory Directory.
     * @throws IOException If directory can not be used as a home directory.
     */
    public void setHome(final File directory) throws IOException {
        if (isSafeAppDirectory(directory)) {
            dir = directory;
            if (App.app != null) {
                App.app.f.getFrame().setTitle(getCanonicalPath(dir));
            }
        } else {
            throw new IOException("Can not use "
                    + Util.getCanonicalPath(directory));
        }
    }

    /**
     * @return Application directory within the base directory.
     */
    public File getHome() {
        if (dir == null) {
            String hd = UtilIO.getHome();
            if (App.app == null) {
                hd = UtilIO.combinePaths(hd, "Panopset");
            }
            File probe = new File(UtilIO.combinePaths(hd, getAppName()));
            if (isSafeAppDirectory(probe)) {
                dir = probe;
                return dir;
            }
            probe = new File(UtilIO.combinePaths(hd, "." + getAppName()));
            while (probe == null || !isSafeAppDirectory(probe)) {
                probe = com.panopset.UtilIO.getDirectoryFromUser(probe,
                        "Please select application base directory");
                if (probe != null && !probe.exists()) {
                    if (!probe.mkdirs()) {
                        throw new RuntimeException(
                                "Could not create directory "
                                + getCanonicalPath(probe));
                    }
                }
                if (probe != null && probe.exists() && probe.isDirectory()) {
                    // user selected, okay to flag
                    flagDirectory(probe);
                    dir = probe;
                    return dir;
                }
            }
        }
        return dir;
    }

    private String getSimpleFlagName() {
        StringWriter sw = new StringWriter();

        sw.append(getAppName());
        sw.append("_READ_ME.txt");
        return sw.toString();
    }

    /**
     * Get flag.
     * @param dir
     * @return
     */
    private File getFlag(final File dir) {
        return new File(UtilIO.combinePaths(
                com.panopset.Util.getCanonicalPath(dir),
                getSimpleFlagName()));
    }

    private void flagDirectory(File dir) {
        File flag = getFlag(dir);
        if (!flag.exists()) {
            LockedWriter lw = new LockedWriter(flag);
            lw.write("This file resides in an application directory for ");
            lw.write(getAppName());
            lw.writeln(".");
            lw.writeln("");
            lw.writeln("Last updated:");
            lw.writeln(com.panopset.Util.getCurrentDateTime());
            lw.close();
        }
    }

    /**
     * Is safe application directory.
     * @param dir Directory to test.
     * @return True if we have a reasonably safe application directory.
     */
    private boolean isSafeAppDirectory(final File dir) {
        if (dir == null) {
            return false;
        }
        if (dir.exists() && dir.isFile()) {
            return false;
        }
        if (dir.exists()) {
            if (dir.isFile()) {
                return false;
            }
            if (trusted) {
                return true;
            }
            if (getFlag(dir).exists()) {
                return true;
            }
            File[] files = dir.listFiles();
            if (files.length == 0) {
                flagDirectory(dir);
                return true;
            }
            return false;
        }
        dir.mkdirs();
        flagDirectory(dir);
        return true;
    }

    /**
     * @return app.txt.
     */
    private File createProps() {
        return new File(UtilIO.combinePaths(getCanonicalPath(getHome()),
                "app.txt"));
    }

    /**
     * Load all data.
     */
    public void load() {
        for (Entry<String, String> e : com.panopset.UtilIO.loadMap(
                createProps()).entrySet()) {
            map.put(e.getKey(), e.getValue());
        }
    }

    /**
     * Save all data.
     */
    public void flush() {
        for (Field field : Field.fields) {
            field.save();
        }
        com.panopset.UtilIO.saveMap(map, createProps());
    }

    /**
     * @param rp Relative path.
     * @return File contents.
     */
    public static final List<String> loadFileFromAppDir(final String rp) {
        return UtilIO.loadFile(new File(UtilIO.combinePaths(s.getHome(),
                rp)));
    }

}
